package bancosys.tec.services.implementor.descriptor;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import jmine.tec.di.injector.impl.MethodBasedInjector;
import jmine.tec.di.metadata.DependencyMetaData;
import jmine.tec.di.scope.TypedScope;
import jmine.tec.di.type.TypeConvertionException;
import jmine.tec.di.validation.StaticValidator;

/**
 * Implementacao do {@link InstanceCreator}
 * 
 * @author MarceloT
 */
public class InstanceCreatorImpl implements InstanceCreator {

    private final Constructor<?> constructor;

    private final DependencyMetaData[] meta;

    /**
     * @param constructor o construtor a ser usado
     * @param resolvers as dependencias
     */
    public InstanceCreatorImpl(Constructor<?> constructor, DependencyMetaData... resolvers) {
        super();
        this.constructor = constructor;
        this.meta = resolvers;
    }

    /**
     * @param constructor o construtor sem dependencias.
     */
    public InstanceCreatorImpl(Constructor<?> constructor) {
        this(constructor, DependencyMetaData.EMPTY_ARRAY);
    }

    /**
     * @param properties props
     * @return object
     * @throws TypeConvertionException e
     */
    public Object hydrateAndCreateInstance(TypedScope properties) throws TypeConvertionException {
        Object[] arguments = new Object[this.meta.length];
        for (int i = 0; i < this.meta.length; i++) {
            Object resolved = this.meta[i].resolveObjectWrap(properties);
            arguments[i] = resolved;
        }
        try {
            return this.constructor.newInstance(arguments);
        } catch (InstantiationException e) {
            throw new IllegalArgumentException(e);
        } catch (IllegalAccessException e) {
            throw new IllegalArgumentException(e);
        } catch (InvocationTargetException e) {
            MethodBasedInjector.rethrowOrWrap(e);
        }
        return null;
    }

    /**
     * @param properties props
     * @return list
     */
    public List<String> validateRequiredProperties(TypedScope properties) {
        return StaticValidator.validateSimple(properties, this.meta);
    }

    /**
     * {@inheritDoc}
     */
    public List<DependencyMetaData> getDependencies() {
        return new ArrayList<DependencyMetaData>(Arrays.asList(this.meta));
    }

}
